#define GLUT_DISABLE_ATEXIT_HACK

#include <iostream> //for graphics
#include <cstdlib>
#include <GL/glut.h>
#include <string>

#include "world.h"
#include "units/cube.h"
#include "units/infantry.h"
#include "objloader.h"

//Functionality exists to test fps on Windows
/*
#include "windows.h"
*/

using namespace std;

World _world;
Model *_map;
Infantry *_infantry;
bool _glSmoothShade = true;
unsigned int _texQual;

//Used to test FPS on Windows
/*
unsigned long _time;
int _fps = 1;
unsigned long _delta = 1000;
*/

void keyboardFunc(unsigned char key, int x, int y)
{
	switch (key)
	{
		case 27:
			exit(0);
			break;
		case 'h':
			_world.getCamera().move(Vec3f(-0.1f, 0.0f, 0.0f));
			break;
		case 'j':
			_world.getCamera().move(Vec3f( 0.0f, 0.0f, 0.1f));
			break;
		case 'k':
			_world.getCamera().move(Vec3f( 0.0f, 0.0f,-0.1f));
			break;
		case 'l':
			_world.getCamera().move(Vec3f( 0.1f, 0.0f, 0.0f));
			break;
		//temporary

		case 'f':
			_world.getCamera().move(Vec3f( 0.0f,-0.01f, 0.0f));
			break;
		case 'g':
			_world.getCamera().move(Vec3f( 0.0f, 0.01f, 0.0f));
			break;
		case 's':
			_glSmoothShade = !_glSmoothShade;
			break;
		case 'u':
			_world.getCamera().rotate(Vec3f( 0.0f, 1.0f, 0.0f));
			break;
  		case 'U':
			_world.getCamera().rotate(Vec3f( 0.0f, -1.0f, 0.0f));
			break;
  		case 'r':
			_world.getCamera().rotate(Vec3f( 1.0f, 0.0f, 0.0f));
			break;
  		case 'R':
			_world.getCamera().rotate(Vec3f( -1.0f, 0.0f, 0.0f));
			break;
	}
}

void passiveMotionFunc(int x, int y)
{
}

void reshapeFunc(int w, int h)
{
	if (h == 0)
		h = 1;
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glViewport(0, 0, w, h);
	gluPerspective(45, ((float)w)/((float)h), 1, 200);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 0.0, 5.0,
		0.0, 0.0,-1.0,
		0.0, 1.0, 0.0);
}

void displayFunc(void)
{
         
     //FPS on Windows
     /*
     if(_delta >= 1000)
     {
	     cout << _fps*25.0 << " fps" << endl;
	     _delta = 0;
	     _fps = 0;
	     _time = GetTickCount();
     }
     
     _fps++;
    */

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	_world.getCamera().point();

	glPushMatrix();
	
	_world.draw();

	glPopMatrix();
	
	glPushMatrix();
		_map->draw();
	glPopMatrix();
	
	_infantry->act();

	glFlush();
	glutSwapBuffers();
	
	//FPS on Windows
	/*
	_delta += (GetTickCount() - _time);
	*/
}

void update(int value)
{
	_world.act();
	glutPostRedisplay();
	glutTimerFunc(5, update, 0);
}

void initRendering()
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glClearColor(0.553f, 0.925f, 1.0f, 1.0f);
	if(_glSmoothShade) glShadeModel(GL_SMOOTH);
	else glShadeModel(GL_FLAT);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_NORMALIZE);

	//Add ambient light
	GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
	
	//Add positioned light
	GLfloat lightColor0[] = {0.7f, 0.7f, 0.7f, 1.0f};
	GLfloat lightPos0[] = { 0.0f, 1.5f, 0.0f, 1.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);

	//Add directed light
	GLfloat lightColor1[] = {1.0f, 0.925f, 0.722f, 1.0f};
	GLfloat lightPos1[] = {0.5f, 2.0f, 1.8f, 1.0f};
	glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1);
	glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

	_world.getCamera().move(Vec3f( 0.0f, 0.05f, 0.0f));

	cout << "Enter texture quality (0,1,2): ";
	cin >> _texQual;

	_map = new Model("../maps/default/map.obj", _texQual);
//	_map = new Model("test/test.obj", 0);

	_infantry = new Infantry(Vec3f(0.0f, 0.2f, 0.0f), Vec3f(0.0f, 1.0f, 0.0f), 0);
	_world.put(_infantry);
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800,600);

	glutCreateWindow("Pulpwn4g3");
	initRendering();

	glutDisplayFunc(displayFunc);
	glutKeyboardFunc(keyboardFunc);
	glutReshapeFunc(reshapeFunc);
	glutPassiveMotionFunc(passiveMotionFunc);

	glutTimerFunc(25, update, 0);
	glutMainLoop();

	return 0;
}
